package com.katesoft.scale4j.persistent.spring.postprocessor;

import com.katesoft.scale4j.common.services.IBeanNameReferences;
import com.katesoft.scale4j.common.spring.AbstractBeanPropertiesOverridePostProcessor;
import com.katesoft.scale4j.common.utils.AssertUtility;
import org.apache.commons.dbcp.BasicDataSource;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.core.Ordered;

import javax.sql.XADataSource;
import java.util.HashMap;
import java.util.Map;

import static com.katesoft.scale4j.common.spring.IPostProcessingOrder.DATASOURCE_POST_PROCESSOR_PRIORITY;

/**
 * The main reason for creating this class is to allow connection details overrides for {@link XADataSource} without changing spring configuration files.
 * <p/>
 * Also this class provides unified solution for overriding datasource bean properties for both {@link BasicDataSource} and {@link
 * com.atomikos.jdbc.nonxa.AtomikosNonXADataSourceBean}
 * - this is necessary because bean's property names don't match.
 * <p/>
 * <p/>
 *
 * @author kate2007
 */
public class DatasourcePostProcessor extends AbstractBeanPropertiesOverridePostProcessor implements Ordered
{
    private String user;
    private String password;
    private String url;
    private String driverClass;
    private int poolInitialSize = -1;
    private int poolMaxSize = -1;
    private String uniqueResourceName;

    public DatasourcePostProcessor()
    {
        setTargetBean(IBeanNameReferences.ACTUAL_DATASOURCE);
        setOrder(DATASOURCE_POST_PROCESSOR_PRIORITY);
    }

    @Override
    protected void doPostProcessing(ConfigurableListableBeanFactory configurableListableBeanFactory)
    {
        BeanDefinition beanDefinition = getBeanDefinition();
        String beanClassName = beanDefinition.getBeanClassName();
        Map<Object, Object> newBeanProperties = new HashMap<Object, Object>();
        if (super.beanProperties != null) { newBeanProperties.putAll(super.beanProperties); }
        if (beanClassName.equalsIgnoreCase("com.atomikos.jdbc.nonxa.AtomikosNonXADataSourceBean")) {
            logger.info("overriding XA datasource bean definition[id=%s, class=%s]", getTargetBean(), beanClassName);
            if (user != null) {newBeanProperties.put("user", user);}
            if (poolInitialSize > 0) {newBeanProperties.put("minPoolSize", poolInitialSize);}
            if (poolMaxSize > 0) {newBeanProperties.put("maxPoolSize", poolMaxSize);}
            if (uniqueResourceName != null) {newBeanProperties.put("uniqueResourceName", uniqueResourceName);}
        }
        else if (beanClassName.equalsIgnoreCase(BasicDataSource.class.getName())) {
            logger.info("overriding basic datasource bean definition[id=%s, class=%s]", getTargetBean(), beanClassName);
            if (user != null) {newBeanProperties.put("username", user);}
            if (poolInitialSize > 0) {newBeanProperties.put("initialSize", poolInitialSize);}
            if (poolMaxSize > 0) {newBeanProperties.put("maxActive", poolMaxSize);}
        }
        //
        if (password != null) {newBeanProperties.put("password", password);}
        if (url != null) {newBeanProperties.put("url", url);}
        if (driverClass != null) {newBeanProperties.put("driverClassName", driverClass);}
        //
        super.beanProperties = newBeanProperties;
        logger.info("new datasource bean properties = %s", newBeanProperties);
        overrideBeanProperties();
    }

    public void setUser(String user)
    {
        this.user = user;
    }

    public void setPassword(String password)
    {
        this.password = password;
    }

    public void setUrl(String url)
    {
        this.url = url;
    }

    public void setDriverClass(Class<?> driverClass)
    {
        this.driverClass = driverClass.getName();
    }

    public void setPoolInitialSize(int poolInitialSize)
    {
        AssertUtility.assertTrue(poolInitialSize > 0, "initial pool size must be positive");
        this.poolInitialSize = poolInitialSize;
    }

    public void setPoolMaxSize(int poolMaxSize)
    {
        AssertUtility.assertTrue(poolMaxSize > 0, "max pool size must be positive");
        this.poolMaxSize = poolMaxSize;
    }

    /**
     * set the unique name of XA datasource(it is possible to have few datasource in context and it is required by Atomikos to set unique names of each).
     *
     * @param uniqueResourceName xa datasource resource name
     *
     * @see com.atomikos.jdbc.nonxa.AtomikosNonXADataSourceBean#setUniqueResourceName(String)
     */
    public void setUniqueResourceName(String uniqueResourceName)
    {
        this.uniqueResourceName = uniqueResourceName;
    }
}
